/**
 * 
 */
package pl.com.like.sequoia.biojava.util.impl;

import java.io.File;
import java.text.MessageFormat;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import pl.com.like.sequoia.annotations.Nullable;
import pl.com.like.sequoia.biojava.enums.UploadFileTypeFormat;
import pl.com.like.sequoia.biojava.pdb.IPdbService;
import pl.com.like.sequoia.biojava.util.IConnectionHelper;
import pl.com.like.sequoia.biojava.util.ILocalFileImporter;
import pl.com.like.sequoia.dao.bio.IBioObjectHandlerDao;
import pl.com.like.sequoia.exceptions.LogicalException;
import pl.com.like.sequoia.model.bio.BioObjectAlign;
import pl.com.like.sequoia.model.bio.BioObjectHandler;
import pl.com.like.sequoia.model.bio.enums.BioObjectType;
import pl.com.like.sequoia.model.bio.enums.FileTypeEnum;
import pl.com.like.sequoia.model.bio.enums.SourceType;

/**
 * @author Pawel Wolanski
 * 
 */
public class LocalFileImporter implements ILocalFileImporter {

	private final static Logger log = LoggerFactory.getLogger(LocalFileImporter.class);

	@Autowired
	private IConnectionHelper connectionHelper;
	@Autowired
	private IBioObjectHandlerDao handlerDao;
	@Autowired
	private IPdbService pdbService;

	/**
	 * Checks if object with given id and type already exists in database.
	 * 
	 * @param idFile
	 * @param fileType
	 * @return
	 */
	public boolean existsInDb(String idFile, FileTypeEnum fileType) {
		boolean exists = false;
		BioObjectHandler obj = handlerDao.get(idFile, fileType);
		if (obj != null) {
			exists = true;
		}
		return exists;
	}

	/**
	 * Checks if object with given id and type already exists in database.
	 * 
	 * @param idFile
	 * @param fileType
	 * @return
	 */
	public boolean existsInDb(String idFile, FileTypeEnum fileType, BioObjectType type) {
		boolean exists = false;
		Object obj = null;
		switch (type) {
			case PROTEIN:
				obj = handlerDao.get(idFile, fileType);
				break;
			case ALIGN:
				obj = handlerDao.get(idFile, fileType);
				break;
		}
		if (obj != null) {
			exists = true;
		}
		return exists;
	}

	/**
	 * Parse file name and suggest possible file type.
	 * 
	 * @param fileName
	 * @return
	 */
	@Nullable
	public UploadFileTypeFormat getFileType(String fileName) {
		UploadFileTypeFormat fileFormat = null;
		// check if is local sequoia file
		if (!fileName.isEmpty() && !fileName.contains(".")) {
			fileFormat = UploadFileTypeFormat.SEQUOIA;
		} else {
			if (fileName.contains("ent.gz")) {
				fileFormat = UploadFileTypeFormat.RCSB;
			}
		}
		// check if is RCSB file
		return fileFormat;
	}

	public IPdbService getPdbService() {
		return pdbService;
	}

	/**
	 * Parse RCSB file name and return protein id.
	 * 
	 * @param incomingFile
	 * @return
	 */
	public String parseRcsbFileObject(File incomingFile) {
		Assert.notNull(incomingFile);
		String fileName = incomingFile.getName();
		String proteinId = fileName.replace(".ent.gz", "");
		proteinId = proteinId.substring(3);
		return proteinId;
	}

	/**
	 * @param inputFile
	 * @return
	 * @throws LogicalException when given object already exists in database
	 */
	public BioObjectHandler preParseFile(File inputFile) throws IllegalArgumentException, LogicalException {
		Assert.notNull(inputFile);
		BioObjectHandler newBioHandler = null;
		// check if file is from RCSB ftp server
		String fileName = inputFile.getName();
		UploadFileTypeFormat fileFormat = getFileType(fileName);
		SourceType type = null;
		if (fileFormat != null) {
			String proteinId = null;
			switch (fileFormat) {
				case RCSB:
					proteinId = parseRcsbFileObject(inputFile);
					type = SourceType.INTERNET;
					break;
				case SEQUOIA:
					proteinId = inputFile.getName();
					type = SourceType.USER;
					break;
			}
			String idFile = proteinId;
			FileTypeEnum fileType = FileTypeEnum.PDB_ZIP;
			File returnFile = inputFile;
			Boolean valid = pdbService.validateFile(returnFile);
			if (Boolean.FALSE.equals(valid)) {
				String warn = MessageFormat.format("file is invalid. Should be in PDB format {0}", returnFile);
				log.error(warn);
				throw new LogicalException(warn);
			} else {
				log.info("file {} is valid PDB format", returnFile);
			}
			if (!existsInDb(idFile, fileType)) {
				connectionHelper.registerFile(idFile, fileType, returnFile, type);
				newBioHandler = handlerDao.get(idFile, fileType);
				log.info(MessageFormat.format("New object with id {0} and type {1} has been added to database", idFile,
						fileType));
			} else {
				String logMsg = MessageFormat.format(
						"object with id {0} and type {1} already exists in database. skipping file {2}", idFile,
						fileType, inputFile.getAbsolutePath());
				log.warn(logMsg);
				throw new LogicalException(logMsg);
			}
		} else {
			throw new LogicalException("Could not recognize type of file.");
		}

		return newBioHandler;
	}

	/**
	 * Save align result in database & in FS repository
	 * 
	 * @param inputFile
	 * @param bioA
	 * @param bioB
	 * @return
	 */
	public BioObjectAlign saveAlign(File inputFile, BioObjectHandler bioA, BioObjectHandler bioB) {
		Assert.notNull(inputFile);
		Assert.notNull(bioA);
		Assert.notNull(bioB);
		final SourceType type = SourceType.USER;

		BioObjectAlign align = null;
		BioObjectType bioType = BioObjectType.ALIGN;
		String idFile = inputFile.getName();
		if (idFile.contains(".")) {
			idFile = idFile.substring(0, idFile.indexOf("."));
		}

		FileTypeEnum fileType = FileTypeEnum.PDB_ZIP;
		File returnFile = inputFile;
		if (!existsInDb(idFile, fileType, bioType)) {
			align = connectionHelper.registerAlign(idFile, fileType, returnFile, type, bioType, bioA, bioB);
			if (log.isDebugEnabled()) {
				log.debug(MessageFormat.format("New object with id {0} and type {1} has been added to database",
						idFile, fileType));
			}
		} else {
			String logMsg = MessageFormat.format(
					"object with id {0} and type {1} already exists in database. skipping file {2}", idFile, fileType,
					inputFile.getAbsolutePath());
			log.warn(logMsg);
			throw new LogicalException(logMsg);
		}
		return align;
	}

	/** {@inheritDoc} */
	public BioObjectHandler setObject(File inputFile) throws LogicalException {
		BioObjectHandler bioHandler = preParseFile(inputFile);
		return bioHandler;
	}

	public void setPdbService(IPdbService pdbService) {
		this.pdbService = pdbService;
	}

}
